/**
 *
 *  Util.Plot.TimeSeriesComponent, V0.7
 *
 *  @author Michael Liljenstam
 *
 */

package SSF.Util.Plot;

import ptolemy.plot.*;


/**
 *  Does a "live" plot of the data file. <br>
 *  One LivePlotter is created per dataset, i.e. one per "graph" in 
 *  a multiplot window.
 *
 *  $Id: LivePlotter.java,v 1.9 2003/02/13 19:32:00 mili Exp $
 */
public class LivePlotter extends MultiPlot {


    ///////////////////////////////////////////////////////////////////
    ////                         constructors                      ////

    /**
     *  Minimal constructor
     */
    public LivePlotter() {

	// standard setup
	setXLabel("time");

	// some defaults
	setBinSize(1.0F);
    }


    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    /** Set bin size. */
    public void setBinSize(float binSize) {
	_binSize = binSize;
	
	if (barGraph) {
	    setBars(_binSize * TimeSeries.BAR_WIDTH, 0);
	    setConnected(false);
	}
    }


    /** Set dataset name for this plot panel. */
    public void setDatasetName(String datasetName) {
	_datasetName = datasetName;

	// legends shrink the graph, have to modify how they're handled
	//  use Ylabel for now.
	if (datasetName != null)
	    setYLabel(datasetName);
	else
	    setYLabel("no filter");
    }


    /** Set dataset name for this plot panel. */

    public void setFieldSelectorName(String fieldSelectorName) {

	// add field selector label if given
	if (fieldSelectorName != null && 
	    !fieldSelectorName.equals(TimeSeries.FIELD_SYMBS[Analyzer.UNSPEC]))
	    addLegend(0, fieldSelectorName); // 0 is just a dummy here
    }


    /** Set time-window size. 
     *  NOTE: bin size must already have been set.
     */
    public void setTimeWindowSize(float winSize) {
	_windowSize = winSize;

	// check if it's a time-window plot and set accordingly
	if (_windowSize > 0) { // time window enabled?
	    // recalculate window size into #bins
	    int windowNumBins = (int) Math.ceil(_windowSize / _binSize);

	    // rescale window
	    setXRange(0, windowNumBins);
	    repaint();

	    // set persistence
	    setPointsPersistence(windowNumBins);
	}
    }

    /** Set number of points to add before rescaling. */
    public void setRescaleStep(int rescaleStep) {
	_rescaleStep = rescaleStep;
    }

    /** 
     *  Set owner application window.
     *  User for signaling to keep state of menu options consistent with 
     *  plot state.
     */
    public void setOwnerAppl(TimeSeriesComponent ownerAppl)
    {
	_ownerAppl = ownerAppl;
    }


    /**
     *  Add one point to plot from the given data array, and given 
     *  data row index.
     *
     *  The x coordinate for the plot is the binIndex * binSize to 
     *  give a proper impression for the transitions in the graph, i.e. 
     *  all state changes during the interval are completed by the end 
     *  of the interval.
     */
    public void addPoint(Object[][] dataArray, int index) {

	if (_stopFlag) {
	//	    System.err.println("plot stopped, index " + index);
	    return;		// plot has been interrupted
	} 
	//	else System.err.println("addPoints called, index " + index);

	if (_firstLoop) {

	    // check that dataset isn't empty
	    if (dataArray[index].length == 0)
		return; // return if empty

	    // plot
	    float value = 0;
	    float xOffset = 0;
	    if (barGraph)
		xOffset = 0.5F;

	    if (dataArray[index][_plotIndex] != null)
		value = ((Float) dataArray[index][_plotIndex]).floatValue();
	    addPoint(0, (_plotIndex + xOffset) * _binSize, value, true);
	    _plotIndex++;	// go to next point to plot
	    if (_plotIndex == dataArray[index].length) { // completed plot?
		// force output of one last point back to zero flows
		if (!barGraph)
		    addPoint(0, (_plotIndex + xOffset) * _binSize, 0, true);
		_plotIndex++;
		_firstLoop = false;	// stop plotting

		// force rescale for last point
		_rescaleCounter = _rescaleStep;

		// signal application that plot has finished
		if (_ownerAppl != null) {
		    _ownerAppl.signalEndOfPlot();
		}
	    }

	    conditionalRescaling();
	}

    } // addPoint()



    /**
     *  Add all points for static plot.
     *  From given array and given data row index.
     *
     *  The x coordinate for the plot is the binIndex * binSize to 
     *  give a proper impression for the transitions in the graph, i.e. 
     *  all state changes during the interval are completed by the end 
     *  of the interval.
     */
    public void addAllPoints(Object[][] dataArray, int index) {

	// plot
	float xOffset = 0;
	if (barGraph)
	    xOffset = 0.5F;

	while (_plotIndex < dataArray[index].length) { 
	    float value = 0;
	    if (dataArray[index][_plotIndex] != null)
		value = ((Float) dataArray[index][_plotIndex]).floatValue();
	    addPoint(0, (_plotIndex + xOffset) * _binSize, value, true);
	    _plotIndex++;	// go to next point to plot
	    if (_plotIndex == dataArray[index].length) { // completed plot?
		// force output of one last point back to zero flows
		if (!barGraph)
		    addPoint(0, (_plotIndex + xOffset) * _binSize, 0, true);
		_plotIndex++;
		_firstLoop = false;	// stop plotting
	    }
	}
	fillPlot();	// rescale plot to fit all points
	
    } // addAllPoints()


    /**
     *  Add a single data point to plot.
     *  <br><br>
     */
    public void addSinglePoint(double time, float value) {

	//	System.err.println("add single pt: " + time + ", val " + value);
	float xOffset = 0;
	if (barGraph)
	    xOffset = 0.5F;
	
	_plotIndex = (int) java.lang.Math.round(time / _binSize);
	addPoint(0, (_plotIndex + xOffset) * _binSize, value, true);

	conditionalRescaling();
	
    } // addSinglePoint()


    /** 
     *  Start this live plot.
     */
    public void startPlot()
    {
	_stopFlag = false; 
    }

    /** 
     *  Interrupt the this live plot.
     */
    public void stopPlot()
    {
	_stopFlag = true; 

	if (_windowSize > 0) { // time-window?
	    _savedXrangeLow = _xlowgiven;
	    _savedXrangeHigh = _xhighgiven;
	}
    }

    /** 
     *  Continue this live plot.
     */
    public void continuePlot()
    {
	if (_windowSize > 0) { // time-window?
	    setXRange(_savedXrangeLow, _savedXrangeHigh); // restore x range
	}

	_stopFlag = false; 
    }

    /** 
     *  Restart live plot from beginning.
     */
    public void resetPlot()
    {
	_plotIndex = 0;		// reset index into data
	_firstLoop = true;
	clear(false);		// clear plot, but keep format parameters
	//	repaint();
	setTimeWindowSize(_windowSize);
    }


    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////

    /**
     *  Rescale plot if certain conditions are met (based on parameters).
     */
    private void conditionalRescaling() {

	if (_rescaleCounter < _rescaleStep) {
	    _rescaleCounter++;
	} else {
	    if (_windowSize > 0) { // time window?
		if(_plotIndex > _windowSize*_binSize) {
		    // enough bars to fill plot yet?

		    fillPlot();
		} else {
		    fillYPlot();
		}
	    } else {
		fillPlot();
	    }
	    _rescaleCounter = 1;
	}
    }

    /* 
     *  Constants
     */


    ///////////////////////////////////////////////////////////////////
    ////                         public variables                 ////



    /** Plot type, default: bar-graph, false = line-graph */
    public  boolean barGraph = true;


    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////

    /** Time bin size. */
    protected float _binSize;

    /** Current plot point. */
    protected int  _plotIndex = 0;     

    /** True if first time plotting points. */
    protected boolean  _firstLoop = true;  // 

    /** Ref to owning application window if there is one. Otherwise null. */
    protected TimeSeriesComponent  _ownerAppl = null;

    /*
     * Attributes
     */

    String    _datasetName = null;

    /** True if the plot has been stopped. (Initialized to true.) */
    boolean   _stopFlag = true;


    /** Window size for sliding time-window plot. In time units according 
        to x-axis. 0 = not a time-window plot. >0 = time-window enabled. */
    private  float         _windowSize = 0;

    /** Rescale live plot after every _rescaleStep data point. */
    private  int           _rescaleStep = 1;

    /** Used for sparse rescaling of plot. */
    private  int           _rescaleCounter = 1;


    /** Saved values of plot range. */
    protected double       _savedXrangeLow;
    /** Saved values of plot range. */
    protected double       _savedXrangeHigh;

} // class LivePlotter


/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
